package de.fhhof.robot.control.cmd.concreate;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import de.fhhof.robot.control.AWorkFlowCommand;
import de.fhhof.robot.control.ApplicationStorage;
import de.fhhof.robot.control.Color;
import de.fhhof.robot.control.ConcreateWrokFlowCommand;
import de.fhhof.robot.control.ExecutionState;
import de.fhhof.robot.control.IControlToRobot;
import de.fhhof.robot.control.IControlToStorage;
import de.fhhof.robot.control.IMessage;
import de.fhhof.robot.control.InvalidOperation;
import de.fhhof.robot.control.Message;
import de.fhhof.robot.control.cmd.AbstractCommand;
import de.fhhof.robot.control.cmd.SAPCommandFactory;
import de.fhhof.robot.control.common.CommonConstants;
import de.fhhof.robot.control.common.MessageUtility;
import de.fhhof.robot.control.common.Utility;
import de.fhhof.robot.control.impl.ControlToJsap;


public class CmdNewAssemblyProcess extends AbstractCommand {

	private IControlToRobot ctrToRobot;
	private IControlToStorage crtToStorage;
	private ControlToJsap ctrToJsap;
	private AssemblyState state;
	private boolean hasAbortSignal = false;

	private List<AWorkFlowCommand> cmdList = null;

	
	public CmdNewAssemblyProcess(IControlToRobot ctrToRobot,
			IControlToStorage crtToStorage,ControlToJsap ctrToJsap) {
		this.ctrToRobot = ctrToRobot;
		this.crtToStorage = crtToStorage;
		this.ctrToJsap =  ctrToJsap;
		this.state = new AssemblyState();
	}	

	private void saveApplicationState(){
		ApplicationStorage.getAssemblyState().store(this.state, CommonConstants.STORAGE_FILE_NAME);
	}
	
	@Override
	public void executeCommand() {

		/** Is there any running assembly process */
		AbstractCommand assemblyCmd = SAPCommandFactory.getCurrentAssemblyCommand();
		
		/** Recover previously running command if any */
		recoverAssemblyState();
		//if(assemblyCmd != null && !this.cmdId.equals(this.state.getCmdId())){
		
		if(assemblyCmd != null){
			this.msg.ack("A001", "");
			System.out.println("Another assembly processing is already running.");
			this.msg.ack("F003", "");
			return;
		}
		SAPCommandFactory.setRunningAssemblyCommand(this);
		
		/** Storing application state */
		this.state.setCmdId(cmdId);
		this.saveApplicationState();
		

		
		/** Storing assembly start time */
		long start = System.currentTimeMillis()/1000;
		if(this.state.isTimeStored()){
			start = this.state.getStartTime();
		}else{
			this.state.setTimeStored(true);
			this.state.setStartTime(start);
			this.saveApplicationState();
		}
		
		/**Acknowledge the message */
		if(!this.state.isAck1Sent()){
			this.msg.ack("A001", "");
		}
		/** Storing application state */
		this.state.setAssemblyMessage(this.msg);
		this.state.setAssemblyRunning(true);
		this.state.setAck1Sent(true);
		this.saveApplicationState();
		

		
		if(!state.isAck2Sent()){
			boolean hasEnoughSmarties = this.crtToStorage.isValidPatternToConstruct(msg,this.state);
			
			/** Has not enough smarties to construct this requested pattern */
			if(!hasEnoughSmarties){
				this.msg.ack("F002", "");
				return;
			}
			
			/** Acknowledge that the pattern is possible to construct */
			this.msg.ack("A002", "");		
			/** Storing application state */
			this.state.setAck2Sent(true);
			this.saveApplicationState();
		}
		
		SAPCommandFactory.setRunningAssemblyCommand(this);
		
		if(!this.state.isColorCmdExist()){
			cmdList = new ArrayList<AWorkFlowCommand>();
			for(Color color: Color.values()){
				AWorkFlowCommand cmd = createWorkFlowCommand(color,msg,this.ctrToRobot,this.crtToStorage);
				if(cmd != null){
					cmdList.add(cmd);
				}
			}
			/** Storing application state */
			this.state.setCmdList(cmdList);
			this.state.setColorCmdExist(true);
			this.saveApplicationState();			
		}
		

		try{
			/** Put an empty smarties palate to transfer point by operator */
			if(!this.state.isOperatorComfirmed()){
				this.ctrToRobot.putEmptyPalleteToTransferPoint(msg);
				this.state.setOperatorComfirmed(true);
				this.saveApplicationState();
			}
			
			/** take smarties palate from tr to sr */
			if(!this.state.isEmptyPalleteExist()){
				this.ctrToRobot.putSmartiePalleteToRobotControl(msg,this.state);
				this.state.setEmptyPalleteExist(true);
				this.saveApplicationState();
			}
			boolean hasError = false;
			for(AWorkFlowCommand cmd: this.state.getCmdList()){
								
				if(!hasAbortSignal){
					if(!cmd.getState().name().toString().equals(ExecutionState.DONE.toString())){
						cmd.setAssemblyState(this.state);
						try{
							IMessage ack3 = cmd.execute();
							cmd.setState(ExecutionState.DONE);
							this.state.getMsgs().add(ack3);							
						}
						catch(InvalidOperation in){
							hasError = true;
							break;
						}
						this.saveApplicationState();
					}

				}
			}
			
			if(!this.state.isSmartyPalleteRemoved()){
				/** Remove the smarties palate from sr to tr */
				this.ctrToRobot.removeSmartiesPaleteFromRobotControl(msg,this.state);	
				this.state.setSmartyPalleteRemoved(true);
				this.saveApplicationState();
			}
			if(hasError){
				throw new InvalidOperation();
			}			
			if(hasAbortSignal){
				System.out.println("Received Abort command from JSAP system");
				System.out.println("Sending acknowledgement :  A004");
				this.msg.ack("A004", "");
			}
			else{
				long end = System.currentTimeMillis()/1000;
				long total = end - start;
				if(total > 999){
					total = 000;
				}
				String strTime = Utility.leftPad(""+total+"", "0", 3);
				
				String ack3Payload = constructReturnValue(this.state.getMsgs());
				ack3Payload += strTime;
				System.out.println("Assembly process completed");
				System.out.println("Sending acknowledgement: A003 , payload: "+ack3Payload);
				this.msg.ack("A003", ack3Payload);
			}



		}
		catch(InvalidOperation ex){
			//create message STJSF001
			String ack3Payload = constructReturnValue(this.state.getMsgs());
			System.out.println(ack3Payload);
			this.msg.ack("F001", ack3Payload);
		}
		
		this.finalizeAssemblyCmd();
		
	}
	private void finalizeAssemblyCmd(){
		SAPCommandFactory.resetCurrentAssemblyCommand();
		//this.state = new AssemblyState();
		//this.saveApplicationState();
		try{
			File file = new File(CommonConstants.STORAGE_FILE_NAME);
			if(file.exists()){
				file.delete();
			}			
		}
		catch(Exception ex){
			System.out.println("Error while deleting recovery file: "+ ex.getMessage());
		}
	}
	private String constructReturnValue(List<IMessage> msgs){
		String pattern = "";
		for(Color color: Color.values()){
			String colorCode = ""+ color.getCode();
			boolean found = false;
			for(IMessage msg: msgs){
				if(msg.getPayload().startsWith(colorCode)){
					pattern += msg.getPayload();
					found = true;
					break;
				}
			}
			if(!found){
				pattern += ""+colorCode+ "00";
			}
		}
		return pattern;
	}
	private static AWorkFlowCommand createWorkFlowCommand(Color color,IMessage msg,IControlToRobot ctR, IControlToStorage ctS){
		
		AWorkFlowCommand cmd = new ConcreateWrokFlowCommand();
		int requiredColor = MessageUtility.getRequiredColor(color, msg.getPayload());
		if (requiredColor == 0) {
			return null;
		}
		cmd.setMessage(msg);
		cmd.setRequiredSmarties(requiredColor);
		cmd.setCompletedSmarties(0);
		cmd.setState(ExecutionState.NOT_STARTED);
		cmd.setColor(color);
		
		return cmd;
		
	}

	@Override
	public void abortCommand() {
		
		this.hasAbortSignal = true;
	}
	
	private void recoverAssemblyState(){

		try{
			AssemblyState state = ApplicationStorage.getAssemblyState().restore(CommonConstants.STORAGE_FILE_NAME);
			
			if(state != null && state.isAssemblyRunning()){
				this.state = state;
			}
		}
		catch(Exception ex){
			System.out.println("Error occured during loading recovery file: "+ex.getMessage());
		}

	}

}
